use crate::AppResult;
use axum::extract::Query;
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::Form;
use openssl::base64::{decode_block, encode_block};
use openssl::hash::MessageDigest;
use openssl::pkey::PKey;
use openssl::rsa::Rsa;
use openssl::sign::Signer;
use serde_json::json;
use std::collections::HashMap;
use std::str::FromStr;
use tokio::sync::OnceCell;
use uuid::Uuid;

const SERVER_ID: &str = "91be2d72-2af6-4046-8e03-b8608cacc093";
static C: OnceCell<Vec<u8>> = OnceCell::const_new();
async fn c() -> &'static Vec<u8> {
    C.get_or_init(|| async {
        let vec = decode_block("MIICXAIBAAKBgQDQ93CP6SjEneDizCF1P/MaBGf582voNNFcu8oMhgdTZ/N6qa6O7XJDr1FSCyaDdKSsPCdxPK7Y4Usq/fOPas2kCgYcRS/iebrtPEFZ/7TLfk39HLuTEjzo0/CNvjVsgWeh9BYznFaxFDLx7fLKqCQ6w1OKScnsdqwjpaXwXqiulwIDAQABAoGATOQvvBSMVsTNQkbgrNcqKdGjPNrwQtJkk13aO/95ZJxkgCc9vwPqPrOdFbZappZeHa5IyScOI2nLEfe+DnC7V80K2dBtaIQjOeZQt5HoTRG4EHQaWoDh27BWuJoip5WMrOd+1qfkOtZoRjNcHl86LIAh/+3vxYyebkug4UHNGPkCQQD+N4ZUkhKNQW7mpxX6eecitmOdN7Yt0YH9UmxPiW1LyCEbLwduMR2tfyGfrbZALiGzlKJize38shGC1qYSMvZFAkEA0m6psWWiTUWtaOKMxkTkcUdigalZ9xFSEl6jXFB94AD+dlPS3J5gNzTEmbPLc14VIWJFkO+UOrpl77w5uF2dKwJAaMpslhnsicvKMkv31FtBut5iK6GWeEafhdPfD94/bnidpP362yJl8Gmya4cI1GXvwH3pfj8S9hJVA5EFvgTB3QJBAJP1O1uAGp46X7Nfl5vQ1M7RYnHIoXkWtJ417Kb78YWPLVwFlD2LHhuy/okT4fk8LZ9LeZ5u1cp1RTdLIUqAiAECQC46OwOm87L35yaVfpUIjqg/1gsNwNsj8HvtXdF/9d30JIM3GwdytCvNRLqP35Ciogb9AO8ke8L6zY83nxPbClM=").unwrap();
        vec
    }).await
}
pub fn registry(app: axum::Router) -> axum::Router {
    app.route("/guid", axum::routing::any(||async {
        Uuid::new_v4().to_string()
    }))
        .route("/jrebel/leases", axum::routing::any(jrebel_leases_handler))
        .route(
            "/jrebel/leases/1",
            axum::routing::any(jrebel_leases1_handler),
        )
        .route(
            "/agent/leases",
            axum::routing::any(jrebel_leases_handler),
        )
        .route(
            "/agent/leases/1",
            axum::routing::any(jrebel_leases1_handler),
        )
        .route(
            "/jrebel/validate-connection",
            axum::routing::any(jrebel_validate_handler),
        )
}
async fn to_lease_create_json(
    client_randomness: &str,
    guid: &str,
    offline: bool,
    valid_from: &str,
    valid_until: &str,
) -> AppResult<String> {
    let server_randomness = "H2ulzLlh7E0=";
    let s2 = if offline {
        [
            client_randomness,
            server_randomness,
            guid,
            offline.to_string().as_str(),
            valid_from,
            valid_until,
        ]
            .join(";")
            .to_string()
    } else {
        [
            client_randomness,
            server_randomness,
            guid,
            offline.to_string().as_str(),
        ]
            .join(";")
            .to_string()
    };
    let rsa = Rsa::private_key_from_der(c().await)?;
    let pkey = PKey::from_rsa(rsa)?;
    let mut signer = Signer::new(MessageDigest::sha1(), &pkey)?;
    signer.update(s2.as_bytes())?;
    let vec = signer.sign_to_vec()?;
    Ok(encode_block(&vec))
}
async fn jrebel_leases_handler(Query(map):Query<HashMap<String, String>>,Form(form_map):Form<HashMap<String, String>>) -> AppResult<impl IntoResponse> {
    let default = String::new();
    let client_randomness =map.get("randomness").unwrap_or(form_map.get("randomness").unwrap_or(&default));
    let username =map.get("username").unwrap_or(form_map.get("username").unwrap_or(&default));
    let guid =map.get("guid").unwrap_or(form_map.get("guid").unwrap_or(&default));
    let client_time =map.get("clientTime").unwrap_or(form_map.get("clientTime").unwrap_or(&default));
    let mut valid_form = "";
    let mut valid_until = "".to_string();
    match u64::from_str(client_time) {
        Ok(client_time_millis) => {
            valid_form = client_time;
            valid_until = (client_time_millis + 180 * 24 * 60 * 60 * 1000).to_string();
        }
        Err(_) => {}
    };

    if client_randomness.is_empty() || username.is_empty() || guid.is_empty() {
        Ok((StatusCode::FORBIDDEN, "".to_string()))
    } else {
        let sign = to_lease_create_json(
            client_randomness,
            guid,
            true,
            valid_form,
            valid_until.as_str(),
        )
        .await?;
        let json = json!({
          "serverVersion": "3.2.4",
          "serverProtocolVersion": "1.1",
          "serverGuid": SERVER_ID,
          "groupType": "managed",
          "id": 1,
          "licenseType": 1,
          "evaluationLicense": false,
          "signature": "OJE9wGg2xncSb+VgnYT+9HGCFaLOk28tneMFhCbpVMKoC/Iq4LuaDKPirBjG4o394/UjCDGgTBpIrzcXNPdVxVr8PnQzpy7ZSToGO8wv/KIWZT9/ba7bDbA8/RZ4B37YkCeXhjaixpmoyz/CIZMnei4q7oWR7DYUOlOcEWDQhiY=",
          "serverRandomness": "H2ulzLlh7E0=",
          "statusCode": "SUCCESS",
          "offline": true,
          "validFrom": valid_form,
          "validUntil": valid_until,
          "company": username,
          "orderId": "",
          "zeroIds": [],
          "licenseValidFrom": valid_form,
          "licenseValidUntil": valid_until,
          "signature": sign
        });
        Ok((StatusCode::OK, json.to_string()))
    }
}
async fn jrebel_leases1_handler(Query(map):Query<HashMap<String, String>>,Form(form_map):Form<HashMap<String, String>>) -> AppResult<impl IntoResponse> {
    let default = String::new();
    let username =map.get("username").unwrap_or(form_map.get("username").unwrap_or(&default));
    let json = json!({
      "serverVersion": "3.2.4",
      "serverProtocolVersion": "1.1",
      "serverGuid": SERVER_ID,
      "groupType": "managed",
      "statusCode": "SUCCESS",
      "msg": "",
      "statusMessage": "",
       "company": username,
    });
    Ok((StatusCode::OK, json.to_string()))
}
async fn jrebel_validate_handler() -> AppResult<impl IntoResponse> {
    let json = json!({
  "serverVersion": "3.2.4",
  "serverProtocolVersion": "1.1",
  "serverGuid": SERVER_ID,
  "groupType": "managed",
  "statusCode": "SUCCESS",
  "company": "Administrator",
  "canGetLease": true,
  "licenseType": 1,
  "evaluationLicense": false,
  "seatPoolType": "standalone"
});
    Ok((StatusCode::OK, json.to_string()))
}